#include <stdio.h>
#include <gmp.h>
#include <string.h>
#include <sys/time.h>
#include <unistd.h>
#include <pthread.h>

//DEFINES

//BOOLEANAS PARA O TESTE DE CONDICOES
#define FALSE  0
#define TRUE 1




//ESTRUTURA DE DADOS COM TODOS OS TERMOS PARA UMA ITERACAO NO METODO DE BORWEIN
typedef struct{
    mpf_t Y[2];
    mpf_t A[2];
    mpf_t PI[2];
    int i;
    int conv;
}termo_borwein;

//PRECISAO = 0,000000000 .... 0000000001 (GLOBAL)
mpf_t prec;



//SE PI_2 - PI_1 < PRECISAO, RETORNA 1 (CONVERGIU) SENAO RETORNA 0 (NAO ESTA NA PRECISAO
int verifica_convergergencia(mpf_t PI_1, mpf_t PI_2)
{
    mpf_t aux;
    int resposta;

    mpf_init(aux);

    // aux = |PI_2 - PI_1|
    mpf_sub(aux, PI_2, PI_1); mpf_abs(aux, aux);

    //COMPARA A PRECISAO
    resposta = (mpf_cmp(aux, prec) <= 0);

    mpf_clear(aux);

    return resposta;
}





//METODO DE BORWEIN SEQUENCIAL PARA CALCULO DO PI
void borwein_sequencial(mpf_t PI, long *it){



    //VAR AUXILIARES
    int i, i0, i1;

    //TERMOS DA ITERACAO
    mpf_t A[2], Y[2];
    //PI ANTIGO
    mpf_t PI_ANTIGO;

     //INICIALIZACAO DE VARIAVEIS COM OS TERMOS DE BORWEIN
     // A0 = 6 - 4 * sqrt(2)
    mpf_init_set_ui(A[0], 2);
    mpf_sqrt(A[0], A[0]);
    mpf_mul_ui(A[0], A[0], 4);
    mpf_ui_sub(A[0], 6, A[0]);

    // Y0 = sqrt(2) - 1
    mpf_init_set_ui(Y[0], 2);
    mpf_sqrt(Y[0], Y[0]);
    mpf_sub_ui(Y[0], Y[0], 1);

    //INICIA AS VARIAVRIS GMP
    mpf_inits(A[1], Y[1], PI_ANTIGO, NULL);

    //ALGORITMO ITERATIVO
    for(i = 0; ; i++)
    {
        //CALCULO DOS INDICES DAS VARIAVEIS Y ANTIGAS E NOVAS
        i0 = (i) % 2;
        i1 = (i + 1) % 2;


        //CALCULO DE Y

        //VARIAVIES AUXILIARES
        mpf_t aux1, aux2, aux3, aux4;
        mpf_inits(aux1, aux2, aux3, aux4, NULL);

        //aux1 = (1 - y0^4) ^ (1/4)
        mpf_pow_ui(aux1, Y[i0], 4);
        mpf_ui_sub(aux1, 1, aux1);
        mpf_sqrt(aux1, aux1);
        mpf_sqrt(aux1, aux1);

        //aux2 = aux1 + 1
        mpf_add_ui(aux2, aux1, 1);

        //y1 = (1 - aux1)/aux2
        mpf_ui_sub(Y[i1], 1, aux1);
        mpf_div(Y[i1], Y[i1], aux2);





        //CALCULO DE A

        int n = 2*i+3;

        //aux3 = y1*(1 +y1 + y1^2)
        mpf_pow_ui(aux3, Y[i1], 2);
        mpf_add(aux3, aux3, Y[i1]);
        mpf_add_ui(aux3, aux3, 1);
        mpf_mul(aux3, aux3, Y[i1]);

        //aux2 = aux1*2^n
        mpf_set_ui(aux4, 2);
        mpf_pow_ui(aux4, aux4, n);
        mpf_mul(aux4, aux4, aux3);

        //a1 = a0*(1 + y1)^4 - aux2
        mpf_add_ui(A[i1], Y[i1], 1);
        mpf_pow_ui(A[i1], A[i1], 4);
        mpf_mul(A[i1], A[i1], A[i0]);
        mpf_sub(A[i1], A[i1], aux4);


        //LIMPA VARIAVEIS AUXILIARES
        mpf_clears(aux1, aux2, aux3, aux4, NULL);


        //SETA NOVO VALOR DE PI
        mpf_set(PI_ANTIGO, PI);


        //CALCULO DE PI

        //Cpi = 1/a
        mpf_ui_div(PI, 1, A[i1]);



        //VERIFICA CONVERGENCIA
        if(verifica_convergergencia(PI_ANTIGO, PI)) {
            break;
        }
    }


    //LIMPA VARIAVEIS
    mpf_clears(A[0], A[1], Y[0], Y[1], PI_ANTIGO, NULL);

    //PASSA O NUMERO DE ITERACOES POR REFERENCIA
    *it = i;

}





int main(int argc, char *argv[])
{
    mpf_t pi;
    long it;
    int time;

    //VARIAVEIS PARA GUARDAR O TEMPO
    struct timeval inicio, fim;

    //SETAR A PRECISAO DO NUMERO DE CASAS DO TIPO DE DADOS MPG
    //EM BITS = (NUM_DIGITS * log2(10) ) = 33219281
    //ISSO EH NECESSARIO, POIS A PRECISAO DA FUNCAO MPG EH DADA EM VALOR DE BITS

    mpf_set_default_prec(33219281);
    // prec = 1/(10^PREC)
    mpf_init_set_ui(prec, 10);
    mpf_pow_ui(prec, prec, 33219281); mpf_ui_div(prec, 1, prec);

    //INICIA VARIAVEL PI
    mpf_init(pi);


    printf("RODANDO METODO DE BORWEIN SEQUENCIAL...\n");

    //PEGA O TEMPO INICIAL DE EXECUCAO
    gettimeofday(&inicio, NULL);


    //METODO PRINCIPAL
    borwein_sequencial(pi, &it);


    //PEGA O TEMPO FINAL DE EXECUCAO
    gettimeofday(&fim, NULL);

    //DIFERENCA ENTRE FINAL E INICIAL
    time = fim.tv_sec - inicio.tv_sec;

    //SALVA O PI EM UM ARQUIVO TXT
    FILE *fp = fopen("borwein_sequencial.txt", "w");
    // IMPREIME COM PRECISAO  DE 10000000 DIGITOS*/
    mpf_out_str(fp, 10, 10000000, pi);
    fclose(fp);



    printf("COMPLETO\nITERACOES: %ld\n", it);
    printf("TEMPO DE EXECUCAO: %d segundos\n", time);

    //LIMPEZA DE VARIAVEIS GMP
    mpf_clear(prec);
    mpf_clear(pi);

    return 0;
}
